//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <core.h>
#include <mantle.h>

//
// The follow two symbols are define in kernel.ld
//
EXTERN_C void _StartUpEntryBegin();
EXTERN_C void _StartUpEntryEnd();

// USER_STARTUP_ENTRY_BASE had defined in asdef.h
#define USER_STARTUP_ENTRY_SIZE    ((UInt32)&_StartUpEntryEnd \
    - (UInt32)&_StartUpEntryBegin)

typedef struct
{
    Boolean (__stdcall *pfnStartUpEntry)(
            PVoid  hDllHandle,
            UInt32   dwReason,
            PVoid   preserved);
    PVoid hDllHandle;
} structEntry_t;

typedef Boolean (__stdcall *pfnWinMainCRTStartUpEntry)(
                            PVoid hInstance,
                            PVoid hPrevInstance,
                            wchar_t * lpCmdLine,
                            int nShowCmd);

#if defined(_GNUC)

// SysExitThread syscall
//
#define SYS_EXIT_THREAD()       \
    do {                        \
        ASM("mov    r0, #0;"    \
            "mov    r5, #0;"    \
            "swi    #0x0b;"     \
            : : :"r0", "r5");   \
    }while (0)

STATIC DECL_SECTION(".startupentry") void UserStartUpEntry(
    structEntry_t *pEntry, UInt32 dwReason)
{
    // The second argument is the alternative sp
    UInt32 *uSP = (UInt32 *)dwReason;
    pEntry = (structEntry_t *)*(uSP + 1);
    dwReason = (UInt32)*(uSP + 2);

    while (pEntry->hDllHandle != NULL) {
        if (FALL_INTO((UInt32)pEntry->hDllHandle, \
                EXE_IMAGE_BASE, EXE_IMAGE_SIZE)) {
            (*(pfnWinMainCRTStartUpEntry)pEntry->pfnStartUpEntry)(
                        pEntry->hDllHandle,  // hInstance
                        NULL,                // hPrevInstance
                        NULL,                // lpCmdLine
                        10);                 // nShowCmd
            break;
        }
        if (FALSE == (*pEntry->pfnStartUpEntry) \
                (pEntry->hDllHandle, dwReason, NULL)) {
            break;
        }
        pEntry--;
    }
    SYS_EXIT_THREAD();
}

#else

EXTERN_C void UserStartUpEntry();

#endif // defined(_GNUC)

EXTERN DECL_CODEINIT bool_t CDECL InitUserEntryTable()
{
    // Page table alredy init in proxy entry (../_marshal/proxyentry.cpp)
    //
    assert(0 != g_initPageDir[PAGEDIR_INDEX(USER_STARTUP_ENTRY_BASE)].uValue);

    assert((UInt32)&_StartUpEntryBegin == (UInt32)&UserStartUpEntry);
    memcpy((void*)USER_STARTUP_ENTRY_BASE, (void *)&UserStartUpEntry,
        USER_STARTUP_ENTRY_SIZE);

    FlushCache();

    return TRUE;
}
